package org.amse.ys.sndeditor.model.modules;

import org.amse.ys.sndeditor.model.IDrawer;
import org.amse.ys.sndeditor.model.IModule;
import org.amse.ys.sndeditor.model.ISoundFile;
import org.amse.ys.sndeditor.model.drawer.AndroidDrawer;

public class WholeDiagramModule implements IModule {
	// MAGIC_NUMBERS_IN_SECONDS / MAGIC_MODIFICATOR has name from MAGIC_NAMES
	public static double[] MAGIC_NUMBERS_IN_SECONDS = { 0.0001, 0.001, 0.01,
			0.1, 1, 15, 30, 60, 120, 300, 600, 3600 };
	public static double[] MAGIC_MODIFICATOR = { 0.001, 0.001, 0.001, 1, 1, 1,
			1, 60, 60, 60, 60, 3600 };
	public static String[] MAGIC_NAMES = { "ms", "ms", "ms", "''", "''", "''",
			"''", "'", "'", "'", "'", " h" };
	public static boolean[] MAGIC_HAS_DECIMALS = { true, false, false, true,
			false, false, false, false, false, false, false, false };

	private IDrawer myDrawer;
	private int myXSize;
	private int myYSize;

	private double myZoom = 1;
	private int myLeftBorder;
	private final String[] MENU_ITEMS = { "Show all", "Remove mark", "Copy",
			"Paste" };
	private ISoundFile mySoundFile;

	// for editing
	boolean myStartIsSelected;
	boolean myEndIsSelected;
	int mySelectionStart;
	int mySelectionEnd;
	long myPreviousTouchTime = 0;

	// Obligatory empty or simple methods
	public void onTimer() {
	}

	public String getMenuItemName(int numberOfItem) {
		return MENU_ITEMS[numberOfItem];
	}

	public int getNumberOfMenuOptions() {
		return MENU_ITEMS.length;
	}

	public void refresh() {
		if (!mySoundFile.isOpened()) {
			return;
		}
		myDrawer.setColor(255, 0, 0, 0);
		myDrawer.drawRectangle(0, 0, myXSize, myYSize);
		int start = myLeftBorder;
		drawGrid(start);
		for (int i = 0; i < mySoundFile.giveNumberOfChannels(); i++) {
			paintHistogram(start, i);
		}
	}

	public void setSize(int width, int height) {
		myXSize = width;
		myYSize = height;
		myDrawer.setSize(width, height);
		refresh();
	}

	public IDrawer getDrawer() {
		return myDrawer;
	}

	public int getTimerDelay() {
		return -1;
	}

	public boolean isMagnifyable() {
		return true;
	}

	public boolean isScreenChanged() {
		return myDrawer.isChanged();
	}

	{ // Initiliser making the drawer
		myDrawer = AndroidDrawer.getDrawer();
	}

	public String getAppletName() {
		return "WholeDiagram module";
	}

	public boolean isSelectable() {
		return true;
	}

	// WARNING! this method should be extremely simple
	public void initilize(ISoundFile fileToProceed) {
		mySoundFile = fileToProceed;
	}

	public void onMagnify(double timesToMagnify) {
		myZoom = myZoom * timesToMagnify;
		if ((mySoundFile.giveLengthInPoints() - this.myLeftBorder) / myZoom < 0.05) {
			myZoom = (mySoundFile.giveLengthInPoints() - this.myLeftBorder) * 20;
		}
		refresh();
	}

	public void onSpin(double spin) {
		if (!mySoundFile.isOpened()) {
			return;
		}
		myLeftBorder += spin * mySoundFile.giveLengthInPoints() / (myZoom);

		if (myLeftBorder < 0) {
			myLeftBorder = 0;
		} else if (myLeftBorder > mySoundFile.giveLengthInPoints()) {
			myLeftBorder = mySoundFile.giveLengthInPoints();
		}
		this.refresh();
	}

	public boolean isSelected() {
		return (myStartIsSelected && myEndIsSelected);
	}

	public int getEndPosition() {
		if (myEndIsSelected) {
			return mySelectionEnd;
		}
		return -1;
	}

	public int getStartPosition() {
		if (myStartIsSelected) {
			return mySelectionStart;
		}
		return -1;
	}

	// Simple methods to implement

	public void onMenuItemSelected(int numberOfMenuOption) {
		switch (numberOfMenuOption) {
		case 0: {
			// show all
			myZoom = 1;
			refresh();
			break;
		}
		case 1: {
			// remove mark
			if (myEndIsSelected) {
				myEndIsSelected = false;
			} else {
				myStartIsSelected = false;
			}
			break;
		}
		case 2: {
			// copy
			if (myStartIsSelected && myEndIsSelected) {
				mySoundFile.copyToClipBoard(mySelectionStart, mySelectionEnd);
			} else {
				// throw new RuntimeException("The area should be selected");
			}
			myStartIsSelected = false;
			myEndIsSelected = false;
			break;
		}
		case 3: {
			// paste
			if (myStartIsSelected) {
				mySoundFile.pasteFromClipBoard(mySelectionStart);
			} else {
				throw new RuntimeException("The paste area should be selected");
			}
			break;
		}
		}
		refresh();
	}

	public void onTouch(int posX, int posY, long time) {
		if (time == myPreviousTouchTime) {
			if (myEndIsSelected) {
				mySelectionEnd = convertXToPoint(posX);
			} else {
				mySelectionStart = convertXToPoint(posX);
			}
		} else {
			myPreviousTouchTime = time;
			if (myStartIsSelected) {
				mySelectionEnd = convertXToPoint(posX);
				myEndIsSelected = true;
			} else {
				mySelectionStart = convertXToPoint(posX);
				myStartIsSelected = true;
			}
		}
		refresh();
	}

	// Quite complicated methods
	private int convertXToPoint(int xPosition) {
		return Math.min((int) (filePointsPerStep() * xPosition + myLeftBorder),
				mySoundFile.giveLengthInPoints());
	}

	private int convertPointToXPosition(int point) {
		return (int) ((point - myLeftBorder) / filePointsPerStep());
	}

	private int convertTimeToXPosition(double d) {
		return convertPointToXPosition(mySoundFile.toPointNumber(d));
	}

	private double filePointsPerStep() {
		return ((mySoundFile.giveLengthInPoints()) / (myXSize * myZoom));
	}

	private void paintHistogram(int start, int chanel) {
		myDrawer.setColor(255, 0, 255, 0);

		int numberOfPoints = mySoundFile.giveLengthInPoints();
		int ySize = myYSize;
		int xSize = myXSize;
		double filePointsPerStep = filePointsPerStep();
		int calibrationFactor = mySoundFile.giveNumberOfChannels()
				* (1 << mySoundFile.giveDepthInBits()) / ySize;
		int calibrationShift = myYSize
				/ (2 * mySoundFile.giveNumberOfChannels());
		calibrationShift += (myYSize * chanel)
				/ (mySoundFile.giveNumberOfChannels());

		if (filePointsPerStep < 1) {
			// we draw every point
			int previousY = calibrationShift;
			int previousX = 0;
			for (int i = start; previousX < xSize; i += 1) {
				int x = (int) ((i - start) / filePointsPerStep);
				int y;
				if (i < numberOfPoints) {
					y = (mySoundFile.giveIntegerIntensityAtPoint(i, chanel)
							/ calibrationFactor + calibrationShift);
				} else {
					y = calibrationShift;
				}

				myDrawer.drawLine(previousX, previousY, (int) x, (int) y);
				previousX = x;
				previousY = y;

			}
		} else if (filePointsPerStep < 20) {
			int intFilePointsPerStep = (int) filePointsPerStep;
			for (int i = 0; i < xSize; i += 1) {
				int maxY = 0;
				int minY = 0;
				int firstPoint = (int) (start + i * filePointsPerStep);
				for (int j = 0; j < intFilePointsPerStep; j++) {
					int y;
					int positionInSoundFile = firstPoint + j;
					if (positionInSoundFile < numberOfPoints) {
						y = (mySoundFile.giveIntegerIntensityAtPoint(
								positionInSoundFile, chanel) / calibrationFactor);
						maxY = Math.max(y, maxY);
						minY = Math.min(y, minY);
					} else {
						break;
					}
					myDrawer.drawLine(i, maxY + calibrationShift, i, minY
							+ calibrationShift);
				}
			}
		} else {
			int previousY = calibrationShift;
			for (int i = 0; i < xSize; i += 1) {
				int y;
				int positionInSoundFile = (int) (start + i * filePointsPerStep);
				if (positionInSoundFile < numberOfPoints) {
					y = (mySoundFile.giveIntegerIntensityAtPoint(
							positionInSoundFile, chanel)
							/ calibrationFactor + calibrationShift);
				} else {
					y = calibrationShift;
				}
				myDrawer.drawLine((i - 1), previousY, i, y);
				previousY = y;
			}
		}

		// drawing selected area
		if (myStartIsSelected && myEndIsSelected) {
			myDrawer.setColor(150, 50, 50, 100);
			int xMin = Math.max(0, convertPointToXPosition(mySelectionStart));
			int xMax = Math.min(myXSize,
					convertPointToXPosition(mySelectionEnd));

			myDrawer.drawRectangle(xMin, 0, xMax, myYSize);
		} else if (myStartIsSelected) {

			myDrawer.setColor(255, 50, 50, 255);
			int x = Math.max(convertPointToXPosition(mySelectionStart), 0);
			x = Math.min(x, myXSize);

			myDrawer.drawLine(x, 0, x, myYSize);
		}

	}

	private String giveGridDescription(double positionInSeconds, int scaleIndex) {
		positionInSeconds = positionInSeconds / MAGIC_MODIFICATOR[scaleIndex];
		String s;
		if (MAGIC_HAS_DECIMALS[scaleIndex]) {
			s = (int) (positionInSeconds) + "."
					+ (int) (positionInSeconds * 10) % 10;
		} else {
			s = "" + (int) (positionInSeconds);
		}
		return (s + " " + MAGIC_NAMES[scaleIndex]);
	}

	private void drawGrid(int start) {
		myDrawer.setColor(150, 255, 255, 255);
		int tmp = myYSize / mySoundFile.giveNumberOfChannels();
		for (int i = 0; i < mySoundFile.giveNumberOfChannels(); i++) {
			myDrawer.drawLine(0, tmp * i + tmp / 2, myXSize, tmp * i + tmp / 2);
		}
		// here we determine the frequency of lines.

		double totalLengthInSeconds = mySoundFile.toSeconds(mySoundFile
				.giveLengthInPoints());
		double lengthInSeconds = totalLengthInSeconds / myZoom;

		int scaleIndex = 0;
		// TODO potentially dangerous
		while (scaleIndex < MAGIC_NUMBERS_IN_SECONDS.length) {
			if (lengthInSeconds / MAGIC_NUMBERS_IN_SECONDS[scaleIndex] > 10) {
				// the scale should be larger
				scaleIndex++;
			} else {
				// more accurate check, counting width
				double gridWidth = MAGIC_NUMBERS_IN_SECONDS[scaleIndex];
				int j = (int) (mySoundFile.toSeconds(start) / gridWidth);
				int countedLength = 0;
				int xPosition = convertTimeToXPosition(j * gridWidth);
				while (xPosition < myXSize) {
					if (xPosition > 0) {
						String text = giveGridDescription(j * gridWidth,
								scaleIndex);
						countedLength += myDrawer.measureTextWidth(text);
					}
					j++;
					xPosition = convertTimeToXPosition(j * gridWidth);
				}
				if (countedLength >= myXSize) {
					scaleIndex++;
				} else {
					break;
				}
			}
		}
		double gridWidth = MAGIC_NUMBERS_IN_SECONDS[scaleIndex];

		gridWidth = MAGIC_NUMBERS_IN_SECONDS[scaleIndex];
		int j = (int) (mySoundFile.toSeconds(start) / gridWidth);
		int xPosition = convertTimeToXPosition(j * gridWidth);
		while (xPosition < myXSize) {
			if (xPosition > 0) {
				String text = giveGridDescription(j * gridWidth, scaleIndex);
				int width = myDrawer.measureTextWidth(text);
				myDrawer.setColor(255, 255, 255, 255);
				if (mySoundFile.giveNumberOfChannels() == 1) {
					myDrawer.drawText(text, xPosition - width / 2, 100);
				} else {
					myDrawer.drawText(text, xPosition - width / 2, myYSize / 2);
				}
				myDrawer.setColor(150, 255, 255, 255);
				myDrawer.drawLine(xPosition, 0, xPosition, myYSize);

			}
			j++;
			xPosition = convertTimeToXPosition(j * gridWidth);
		}

	}

	public void onDPadKeyPressed() {
	}

	public int getMenuItemIconIndex(int numberOfItem) {
		return -1;
	}

	@Override
	public ISoundFile getSoundFile() {
		// TODO Auto-generated method stub
		return null;
	}

}
